// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: leon
#pragma once

#include <iostream>
#include <memory>
#include <optional>
#include <set>
#include <sstream>
#include <string>
#include <vector>

#include <boost/date_time/posix_time/posix_time.hpp>

#include "coin2/exchange/base/symbology/derivative_util.h"
#include "coin2/exchange/base/symbology/futures_product.h"
#include "coin2/exchange/bitflyer_futures/symbology/currency.h"

namespace coin2::exchange::bitflyer_futures::symbology {

using coin2::exchange::bitflyer_futures::symbology::BitflyerCurrency;

class BitflyerFuturesProduct
    : public base::symbology::FuturesProduct<BitflyerCurrency, BitflyerFuturesProduct>,
      public base::symbology::ProductFromStrMixin<BitflyerFuturesProduct> {
 public:
  using FuturesProduct = base::symbology::FuturesProduct<BitflyerCurrency, BitflyerFuturesProduct>;
  using ContractType = base::symbology::ContractType;

  BitflyerFuturesProduct(
      const BitflyerCurrency& base,
      const BitflyerCurrency& quote,
      const std::string& native_product,
      const std::string& native_subscription_symbol,
      const ContractType contract_type,
      const int64_t expiry_timestamp,
      const int64_t resolve_timestamp)
      : FuturesProduct(
            base,
            quote,
            native_product,
            native_subscription_symbol,
            base::market::ExchangeType::Bitflyer,
            contract_type,
            expiry_timestamp,
            resolve_timestamp) {}

 public:
  static std::unique_ptr<BitflyerFuturesProduct> FromNativeStrImpl(
      const std::string& native_product,
      const int64_t resolve_timestamp) {
    std::unique_ptr<BitflyerCurrency> base;
    std::unique_ptr<BitflyerCurrency> quote;
    std::string expiry_str;

    std::vector<std::string> splitted;
    splitted = Split(native_product, "_");
    if (splitted.size() == 3) {
      CHECK_EQ(splitted[0], "FX") << native_product;
      base = BitflyerCurrency::FromNativeStrNoThrow(splitted[1], resolve_timestamp);
      quote = BitflyerCurrency::FromNativeStrNoThrow(splitted[2], resolve_timestamp);
      expiry_str = "PERPETUAL";
    } else if (splitted.size() == 2) {
      base = BitflyerCurrency::FromNativeStrNoThrow(splitted[0], resolve_timestamp);
      quote = BitflyerCurrency::FromNativeStrNoThrow(splitted[1], resolve_timestamp);
      expiry_str = "IMMEDIATE";
    } else {
      return {};
    }
    CHECK(base);
    CHECK(quote);

    std::string native_product_tmp;
    std::string native_subscription_symbol;
    ContractType contract_type;
    int64_t expiry_timestamp;
    bool success = CalculateParams(
        *base,
        *quote,
        expiry_str,
        resolve_timestamp,
        &native_product_tmp,
        &native_subscription_symbol,
        &contract_type,
        &expiry_timestamp);
    if (success) {
      DCHECK_EQ(native_product_tmp, native_product);
      return std::make_unique<BitflyerFuturesProduct>(
          *base,
          *quote,
          native_product,
          native_subscription_symbol,
          contract_type,
          expiry_timestamp,
          resolve_timestamp);
    } else {
      return {};
    }
  }

  static bool CalculateParams(
      const BitflyerCurrency& base,
      const BitflyerCurrency& quote,
      const std::string& expiry_str,
      const int64_t resolve_timestamp,
      std::string* native_product,
      std::string* native_subscription_symbol,
      ContractType* contract_type,
      int64_t* expiry_timestamp) {
    using base::symbology::ContractType;
    using base::symbology::StringToContractType;

    auto type = StringToContractType(expiry_str);
    if (type) {
      *contract_type = type.value();
      if (*contract_type == ContractType::PERPETUAL) {
        *expiry_timestamp = FuturesProduct::PERPETUAL_TIMESTAMP;
      } else if (*contract_type == ContractType::IMMEDIATE) {
        *expiry_timestamp = FuturesProduct::IMMEDIATE_TIMESTAMP;
      } else {
        NOTREACHED();
      }
    } else {
      return false;
    }

    std::stringstream ss;
    if (*contract_type == ContractType::PERPETUAL) {
      ss << "FX_" << base.native_currency() << '_' << quote.native_currency();
    } else if (*contract_type == ContractType::IMMEDIATE) {
      ss << base.native_currency() << '_' << quote.native_currency();
    } else {
      NOTREACHED();
    }
    *native_product = ss.str();
    *native_subscription_symbol = *native_product;
    return true;
  }

  static const int64_t expiry_time_of_day_nanosec = 12 * 3600 * 1e9;
};

}  // namespace coin2::exchange::bitflyer_futures::symbology
